package com.chungkui.check.aspect;

import com.chungkui.check.annotation.Check;
import com.chungkui.check.configparser.bean.CheckConfig;
import com.chungkui.check.core.CheckChain;
import com.chungkui.check.core.ModelContainer;
import com.chungkui.check.core.MsgBuilder;
import com.chungkui.check.core.MsgModel;
import com.chungkui.check.core.bean.CheckResult;
import com.chungkui.check.exception.CheckFailException;
import com.chungkui.check.expression.CheckConfigStore;
import com.chungkui.check.paramparser.ParamParserEngine;
import com.chungkui.check.util.AspectUtil;
import com.chungkui.check.util.CheckResultContainer;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * Copyright (C), 2002-2019,草帽团开发团队
 * 〈注解式校验切面类〉<br>
 * 〈功能详细描述〉
 *
 * @author jason
 * @fileName: CheckAspect.java
 * @date: 2019/6/28 12:54
 * @see [相关类/方法](可选)
 * @since [产品/模块版本] (可选)
 */
@Aspect
public class CheckAspect {
    private final Logger log = LoggerFactory.getLogger(CheckAspect.class);
    @Autowired
    private ModelContainer modelContainer;
    @Autowired
    private CheckChain checkChain;
    @Autowired
    private MsgBuilder msgBuilder;
    @Autowired
    private CheckConfigStore checkConfigCacheService;

    public CheckAspect() {
        log.info("Welcome to the chungkui check,init success");
    }

    @Pointcut("@annotation(com.chungkui.check.annotation.Check)")
    public void checkPointcut() {
        throw new UnsupportedOperationException();
    }


    @Around("checkPointcut()")
    public Object checkAround(ProceedingJoinPoint joinPoint) throws Throwable {
        /*获取注解*/
        Check check = getAnnotation(joinPoint);
        if (check != null) {
            String model = check.model();
            /*取参数模式*/
            ParamParserEngine paramParserEngine = modelContainer.obtainModel(model, joinPoint);
            String configKey = getMethodKey(joinPoint, check);
            CheckConfig checkConfig = checkConfigCacheService.get(configKey);
            if (checkConfig == null) {
                checkConfig = checkConfigCacheService.parseCheckConfig(check, configKey);
            }
            Map<String, Object> params = paramParserEngine.parserParam2Map(joinPoint);
            CheckResult checkResult = checkChain.doCheck(checkConfig, params);
            Class clazz=getReturnType(joinPoint);
            if (!checkResult.ifPass()) {
                Object msg =  msgBuilder.buildFailMsg(checkResult, check,clazz);
                MsgModel msgModel = check.msgModel() == MsgModel.AUTO ? paramParserEngine.getMsgModel() : check.msgModel();
                switch (msgModel) {
                    case EXCEPTION:
                        throw new CheckFailException( msg, checkResult);
                    case TRANSFER:
                        CheckResultContainer.setFailMsg(msgBuilder.buildFailMsg(checkResult, check,clazz));
                        break;
                    default:
                        return msg;
                }
            }
        }
        return msgBuilder.buildSuccessMsg(check, joinPoint.proceed());
    }

    /**
     * 在指定切入点发生异常时，打印异常日志
     *
     * @param joinPoint
     * @param ex
     */
    @AfterThrowing(pointcut = "checkPointcut()", throwing = "ex")
    public void afterThrowsAdvice(JoinPoint joinPoint, Exception ex) {
        String errorInfo = joinPoint.getTarget().getClass() + "." + joinPoint.getSignature().getName() + "()" + "发生异常";
        log.error(errorInfo);
        if (log.isDebugEnabled()) {
            log.error(ex.getLocalizedMessage(), ex);
        }
    }

    /**
     * 获取方法中的注解信息
     */
    private Check getAnnotation(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(Check.class);
        }
        return null;
    }

    /**
     * 获取方法中的注解信息
     */
    private String getMethodKey(JoinPoint joinPoint, Check check) {
        String remote = check.remote();
        if (StringUtils.isNotEmpty(remote)) {
            return remote;
        }
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        return AspectUtil.getMethodKey(method);
    }
    private Class getReturnType(JoinPoint joinPoint) {
        return AspectUtil.getReturnType(joinPoint);
    }
}
